package com.example.zxw.ui.views.smartrefresh

import androidx.compose.animation.core.Animatable
import androidx.compose.foundation.MutatorMutex
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.zIndex
import com.example.zxw.ui.views.smartrefresh.header.CnsHeader
import com.fmt.mvi.learn.gobal.log
import kotlinx.coroutines.launch

enum class RefreshState {
    PULL_REFRESH,//下拉去刷新状态
    RELEASE_REFRESH,//释放刷新状态
    REFRESHING,//刷新中状态

}

@Composable
fun CnsSmartRefresh(
    isFreshing: Boolean,
    header: @Composable (state: RefreshState) -> Unit = {
        CnsHeader(state = it)
    },
    content: @Composable () -> Unit,
    onRefresh: () -> Unit
) {
    val mutatorMutex = MutatorMutex()
    val coroutineScope = rememberCoroutineScope()
    var refreshState by remember {
        mutableStateOf(RefreshState.PULL_REFRESH)
    }
    //头部偏移位置
    var heardOffset = remember {
        Animatable(0f)
    }

    var headerHeight by remember { mutableStateOf(0) }
    val maxOffset = 1000
    val refreshTrigger = headerHeight * 1.2//刷新触发时机

    suspend fun animateOffsetTo(offset: Float) {
        mutatorMutex.mutate {
            heardOffset.animateTo(offset)
        }
    }
    LaunchedEffect(key1 = isFreshing, block = {
        if (isFreshing) {
            "LaunchedEffect 刷新中 heardOffset = ${heardOffset}".log()
            refreshState = RefreshState.REFRESHING
            animateOffsetTo(headerHeight.toFloat())

        } else {
            //刷新完成,还原位置及刷新状态是
            "LaunchedEffect 恢复 heardOffset = ${heardOffset}".log()
            refreshState = RefreshState.PULL_REFRESH
            animateOffsetTo(0f)

        }
    })

    Box(
        modifier = Modifier
            .fillMaxSize()
            .nestedScroll(object : NestedScrollConnection {
                var dampingFactor = .5f//阻尼系数
                override fun onPreScroll(
                    available: Offset,
                    source: NestedScrollSource
                ): Offset {
                    //上划的时候判断是否需要拦截（本身是否有偏移距离）
                    if (available.y < 0 && heardOffset.value > 0) {
                        var selfConsumed = 0f
                        var temp = false
                        //将需要拦截的部分拦截，多余的不要
                        if (heardOffset.value + available.y * dampingFactor < 0) {
                            selfConsumed = 0 - heardOffset.value
                            temp = true
                        } else {
                            selfConsumed = available.y * dampingFactor
                        }
                        coroutineScope.launch {
                            heardOffset.snapTo(heardOffset.value + selfConsumed)
                        }
                        //更改当前刷新状态,如果是刷新中则保持
                        if (heardOffset.value <= refreshTrigger && refreshState != RefreshState.REFRESHING) {
                            refreshState = RefreshState.PULL_REFRESH
                        }
                        return Offset(
                            0f,
                            if (temp) selfConsumed else available.y
                        )
                    }
                    return Offset.Zero
                }

                override fun onPostScroll(
                    consumed: Offset,
                    available: Offset,//子view消费后还有多少剩余
                    source: NestedScrollSource
                ): Offset {
                    //如果是刷新中，则可以下拉到刷新中的区域
                    if (refreshState == RefreshState.REFRESHING && available.y >= 0) {
                        var selfConsumed = 0f
                        if (heardOffset.value + available.y >= headerHeight) {
                            selfConsumed = headerHeight - heardOffset.value
                        } else {
                            selfConsumed = available.y
                        }
                        coroutineScope.launch {
                            heardOffset.snapTo(heardOffset.value + selfConsumed)
                        }
                        return Offset(0f, selfConsumed)
                    } else {
                        if (source == NestedScrollSource.Drag && available.y >= 0) {//子view下拉滚动到头后
                            //设置自己的偏移量
                            var selfConsumed = 0f
                            var temp = false
                            if (heardOffset.value + available.y * dampingFactor > maxOffset) {//如果超出了需要的滑动，则只消费自己需要的
                                selfConsumed = maxOffset - heardOffset.value
                                temp = true
                            } else {//都在接受范围内
                                selfConsumed = available.y * dampingFactor
                            }
                            coroutineScope.launch {
                                heardOffset.snapTo(heardOffset.value + selfConsumed)
                            }

                            //更改当前刷新状态
                            if (heardOffset.value >= refreshTrigger && refreshState != RefreshState.REFRESHING) {
                                refreshState = RefreshState.RELEASE_REFRESH
                            }
                            return Offset(
                                0f, if (temp) {
                                    selfConsumed
                                } else {
                                    available.y
                                }
                            )
                        }
                    }
                    return super.onPostScroll(consumed, available, source)
                }


                override suspend fun onPreFling(available: Velocity): Velocity {
                    //手势抬起后会触发该方法
                    if (refreshState != RefreshState.REFRESHING && heardOffset.value > 0) {

                        if (refreshState == RefreshState.RELEASE_REFRESH) {//触发刷新，告诉调用者，
                            "手势抬起，进入刷新状态".log()
                            animateOffsetTo(headerHeight.toFloat())
                            refreshState = RefreshState.REFRESHING
//                            isFreshing = true
                            onRefresh()
                        } else if (refreshState == RefreshState.PULL_REFRESH) {//未触发刷新，状态不改变，位置还原
                            "手势抬起，进入恢复".log()
                            animateOffsetTo(0f)
                        }
                    }
                    return super.onPreFling(available)
                }
            })
    ) {
        Box(modifier = Modifier
            .onGloballyPositioned {
                headerHeight = it.size.height
            }
            .let { if (heardOffset.value.toInt() - headerHeight < 0) it.clipToBounds() else it }
            .zIndex(0f)
            .offset {
                IntOffset(0, heardOffset.value.toInt() - headerHeight)
            }) {
            Box(
                modifier = Modifier
                    .wrapContentSize()
                    .fillMaxWidth()
            ) {
                header(refreshState)
            }
        }
        Box(modifier = Modifier.offset {
            IntOffset(0, heardOffset.value.toInt())
        }) {
            content()
        }
    }

}




